package com.meida.common.interceptor;

/**
 * @author flyme
 * @date 2019/10/26 18:31
 */

import com.meida.common.annotation.RlockRepeat;
import com.meida.common.lock.LockConstant;
import com.meida.common.lock.RedissonLockerImpl;
import com.meida.common.security.OpenHelper;
import com.meida.common.utils.ApiAssert;
import com.meida.common.utils.FlymeUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 防止重复提交分布式锁拦截器
 *
 * @author 2019年6月18日
 */
@Aspect
@Component
public class RlockRepeatSubmitAspect {

    @Resource
    private RedissonLockerImpl redissonLocker;

    /***
     * 定义controller切入点拦截规则，拦截RlockRepeatSubmit注解的业务方法
     */
    @Pointcut("@annotation(com.meida.common.annotation.RlockRepeat)")
    public void pointCut() {
    }

    /**
     * AOP分布式锁拦截
     *
     * @param joinPoint
     * @return
     * @throws Exception
     */
    @Around("pointCut()")
    public Object rlockRepeatSubmit(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取类里面的方法
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method targetMethod = methodSignature.getMethod();
        RlockRepeat repeatSubmit = targetMethod.getAnnotation(RlockRepeat.class);
        if (Objects.nonNull(repeatSubmit)) {
            // 获取参数
            Object[] args = joinPoint.getArgs();
            // 进行一些参数的处理，比如获取订单号，操作人id等
            String orderNo = "";
            Long accessToken = OpenHelper.getUserId();
            HttpServletRequest request = getRequest();
            if (null != request) {
                StringBuffer lockKeyBuffer = new StringBuffer();
                LockConstant lockConstant = repeatSubmit.lockConstant();
                lockKeyBuffer.append(lockConstant.getKeyPrefix());
                lockKeyBuffer.append(repeatSubmit.prefix());
                // 使用用户的token和请求路径作为唯一的标识，如果有orderNo，加上orderNo作为唯一标识
                if (FlymeUtils.isNotEmpty(accessToken)) {
                    lockKeyBuffer.append(accessToken.hashCode());
                }
                // 公平加锁，lockTime后锁自动释放
                boolean isLocked = false;
                try {
                    isLocked = redissonLocker.fairLock(lockKeyBuffer.toString(), TimeUnit.SECONDS, lockConstant);
                    // 如果成功获取到锁就继续执行
                    if (isLocked) {
                        // 执行进程
                        return joinPoint.proceed();
                    } else {
                        // 未获取到锁
                        ApiAssert.failure("请不要重复请求");
                    }
                } finally {
                    // 如果锁还存在，在方法执行完成后，释放锁
                    if (isLocked) {
                        redissonLocker.unlock(lockKeyBuffer.toString());
                    }
                }
            }
        }
        return joinPoint.proceed();
    }

    public static HttpServletRequest getRequest() {
        ServletRequestAttributes ra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return ra.getRequest();
    }

}
